﻿///-----------------------------------------------------------------------
/// <copyright file="LiquidationViewModel.cs" company="FactSet Research Systems, Inc.">
///     Copyright © FactSet Research Systems, Inc.  All rights reserved 2012.
/// </copyright>
/// <author>Jonathan K. Barez</author>
///-----------------------------------------------------------------------
namespace HousholdISv2.ViewModel.Transaction
{
    using System;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Linq;
    using System.Transactions;
    using System.Windows;
    using System.Windows.Controls;
    using Core;
    using DataRepository;
    using MVVMCore;
    using MVVMCore.ViewModel;
    using MVVMCore.ViewModel.Details;

    public class LiquidationViewModel : CollectionViewModel<ViewModelBase>, IHasItemCollection
    {
        #region Fields

        private Action cancelAction;

        #endregion Fields

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="LiquidationViewModel"/> class.
        /// </summary>
        public LiquidationViewModel()
        {
            this.Details = new ObservableCollection<ViewModelBase>();
        }
        #endregion Constructors

        #region Events

        #endregion Events

        #region Properties

        public string DisplayName
        {
            get { return "Liquidation"; }
        }

        public double ListMinWidth
        {
            get { return GetValue(() => ListMinWidth, 300); }
            set { SetValue(() => ListMinWidth, value); }
        }

        public double DetlMinWidth
        {
            get { return GetValue(() => DetlMinWidth, 250); }
            set { SetValue(() => DetlMinWidth, value); }
        }

        public bool IsSelected
        {
            get { return GetValue(() => IsSelected); }
            set { SetValue(() => IsSelected, value); }
        }

        public bool IsInSelectionMode
        {
            get { return GetValue(() => this.IsInSelectionMode, true); }
            set { SetValue(() => this.IsInSelectionMode, value); }
        }

        public DatePickerViewModel Month
        {
            get { return GetValue(() => this.Month) ?? this.InitializeMonth(); }
            set { SetValue(() => this.Month, value); }
        }

        public LiquidationListViewViewModel List
        {
            get { return GetValue(() => this.List) ?? this.InitializeList(); }
            set { SetValue(() => this.List, value); }
        }

        public CommandCollectionViewModel CommandCollection
        {
            get { return GetValue(() => this.CommandCollection) ?? this.InitializeCommandCollection(); }
            set { SetValue(() => this.CommandCollection, value); }
        }

        public ObservableCollection<ViewModelBase> Details
        {
            get { return GetValue(() => this.Details); }
            set { SetValue(() => this.Details, value); }
        }

        #region Commands

        private CommandViewModel NewCommand
        {
            get { return GetValue(() => this.NewCommand) ?? this.InitializeNewCommand(); }
            set { SetValue(() => this.NewCommand, value); }
        }

        private CommandViewModel SaveCommand
        {
            get { return GetValue(() => this.SaveCommand) ?? this.InitializeSaveCommand(); }
            set { SetValue(() => this.SaveCommand, value); }
        }

        private CommandViewModel CancelCommand
        {
            get { return GetValue(() => this.CancelCommand) ?? this.InitializeCancelCommand(); }
            set { SetValue(() => this.CancelCommand, value); }
        }

        #endregion Commands

        #endregion Properties

        #region Methods

        public void RefreshList()
        {
            this.IsInSelectionMode = true;
            Refresh(this.List, this.Month.Value ?? GetCurrent());
        }

        protected override ObservableCollection<ViewModelBase> InitializeItems()
        {
            return
                new ObservableCollection<ViewModelBase>
                    {
                        this.Month,
                        this.List,
                    };
        }

        private static void Refresh(LiquidationListViewViewModel list, DateTime month)
        {
            var last = list.SelectedData;
            list.ClearItems();
            var start = new DateTime(month.Year, month.Month, 1);
            var end = start.AddMonths(1).AddMilliseconds(-1);
            using (var dc = Context.GetContextInstance())
            {
                var items = (from e in dc.Expenses
                             where e.Date >= start && e.Date <= end
                             orderby e.Date, e.Id
                             select
                                 new
                                     {
                                         Data = e,
                                         ExpenseDescription = e.ExpenseType.Description
                                     }).ToArray();
                list.AddRange(items.Select(item => item.Data));
            }

            if (last == null)
            {
                return;
            }

            var found = list.Single(item => item.Data.Id == last.Id);
            if (found != null &&
                !found.IsSelected)
            {
                found.IsSelected = true;
            }
        }

        private DatePickerViewModel InitializeMonth()
        {
            var dpvm =
                new DatePickerViewModel
                    {
                        Caption = "Month:",
                        CaptionVisibility = Visibility.Visible,
                        IsMonthYear = true,
                        DateFormat = "MMMM, yyyy",
                        Margin = new Thickness(0, 0, 0, 5),
                        Value = GetCurrent(),
                        IsReadOnly = false,
                    };
            dpvm.ValueChanged += MonthValueChanged;
            return this.Month = dpvm;
        }

        private LiquidationListViewViewModel InitializeList()
        {
            var lvvm = new LiquidationListViewViewModel();
            lvvm.HighlightedForeground = lvvm.Foreground;
            lvvm.SelectionChanged += ListSelectionChanged;
            Refresh(lvvm, this.Month.Value ?? GetCurrent());
            this.ResetCommands();
            return this.List = lvvm;
        }

        private void ListSelectionChanged(object sender, SelectionChangedEventArgs<LiquidationListViewItemViewModel> e)
        {
            this.ClearDetails();
            if (e.Current == null)
            {
                return;
            }

            var data = e.Current.Data;
            var detail =
                new RecordDetailViewModel(
                    new TextBoxViewModel
                        {
                            Caption = "Transaction Id:",
                            Value =
                                data.Id == 0
                                    ? string.Empty
                                    : data.Id.ToString(CultureInfo.InvariantCulture),
                            IsReadOnly = true
                        },
                    new TextBoxViewModel
                        {
                            Caption = "Transaction Date:",
                            Value = data.Date.ToString("MMMM dd, yyyy"),
                            IsReadOnly = true
                        },
                    new DatePickerViewModel
                        {
                            Caption = "Transaction Date:",
                            Value = data.Date,
                            DateFormat = "MMMM dd, yyyy",
                            IsReadOnly = false,
                            PropertyName = this.GetPropertyName(() => data.Date),
                        },
                    new TextBoxViewModel
                        {
                            Caption = "Expense Type:",
                            Value = e.Current.ExpenseType,
                            IsReadOnly = true
                        },
                    new ComboBoxViewModel<ExpenseType>("Expense Type:")
                        {
                            IsReadOnly = false,
                        },
                    new TextBoxViewModel
                        {
                            Caption = "Comment:",
                            Value = data.Comment,
                            LineCount = 3,
                            IsReadOnly = true,
                            PropertyName = this.GetPropertyName(() => data.Comment),
                        },
                    new TextBoxViewModel
                        {
                            Caption = "Amount:",
                            Value = data.Amount.ToString("#,##0.00"),
                            IsReadOnly = true,
                            PropertyName = this.GetPropertyName(() => data.Amount),
                        })
                    {
                        Validator =
                            control =>
                                {
                                    if (!this.IsInSelectionMode)
                                    {
                                        switch (control.Caption)
                                        {
                                            case "Transaction Date:":
                                                var dpvm = (DatePickerViewModel) control;
                                                if (!dpvm.Value.HasValue)
                                                {
                                                    return "Transaction date is required!";
                                                }
                                                break;
                                            case "Expense Type:":
                                                var cbvm = control as ComboBoxViewModel<ExpenseType>;
                                                if (cbvm != null &&
                                                    cbvm.SelectedItem == null)
                                                {
                                                    return "Expense type entry is required!";
                                                }
                                                break;
                                            case "Comment:":
                                                var tbvm = (TextBoxViewModel) control;
                                                if (string.IsNullOrWhiteSpace(tbvm.Value))
                                                {
                                                    return "Please provide a brief description of the expense.";
                                                }
                                                break;
                                            case "Amount:":
                                                tbvm = (TextBoxViewModel) control;
                                                decimal result;
                                                if (!decimal.TryParse(tbvm.Value, out result) || result == 0)
                                                {
                                                    return "Please enter a valid number for the amount!";
                                                }
                                                break;
                                        }
                                    }

                                    return string.Empty;
                                }
                    };
            detail.IsValidChecker = 
                () => detail.CanUpdate;
            detail.FindCaptionedModel<DatePickerViewModel>("Transaction Date:").Visibility = Visibility.Collapsed;
            detail.FindCaptionedModel<ComboBoxViewModel<ExpenseType>>("Expense Type:").Visibility = Visibility.Collapsed;
            this.Details.Add(detail);
            this.ResetCommands();
        }

        private void MonthValueChanged(object sender, EventArgs e)
        {
            this.RefreshList();
        }

        private static DateTime GetCurrent()
        {
            return new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
        }

        private CommandCollectionViewModel InitializeCommandCollection()
        {
            var ccvm =
                new CommandCollectionViewModel
                {
                    Orientation = Orientation.Vertical,
                    VerticalAlignment = VerticalAlignment.Top,
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    CommandSpacing = 5,
                };
            ccvm.AddRange(
                this.NewCommand,
                this.SaveCommand,
                this.CancelCommand);
            return this.CommandCollection = ccvm;
        }

        #region Commands

        private CommandViewModel InitializeNewCommand()
        {
            var cvm = new CommandViewModel("New", param => this.DoCreateNew());
            return this.NewCommand = cvm;
        }

        private CommandViewModel InitializeSaveCommand()
        {
            var cvm = new CommandViewModel(
                "Save",
                param => this.DoSave(),
                param =>
                {
                    var maint = this.Details.OfType<RecordDetailViewModel>().FirstOrDefault();
                    return maint != null && maint.IsValid;
                });
            return this.SaveCommand = cvm;
        }

        private CommandViewModel InitializeCancelCommand()
        {
            var cvm = new CommandViewModel("Cancel", param => this.DoCancel());
            return this.CancelCommand = cvm;
        }

        #endregion Commands

        private void DoCreateNew()
        {
            this.ClearDetails();
            var last = this.List.SelectedItem;

            var newExpense = new Expense { Date = DateTime.Now };
            this.List.RaiseSelectionChangedEvent = false;
            var lvivm = this.List.Add(newExpense);
            lvivm.IsSelected = true;
            lvivm.Description = "<New Transaction>";
            this.List.RaiseSelectionChangedEvent = true;
            this.List.InvokeSelectionChanged();
            this.IsInSelectionMode = false;

            var detail = this.Details.OfType<RecordDetailViewModel>().First();
            detail.FindCaptionedModel<TextBoxViewModel>("Transaction Date:").Visibility = Visibility.Collapsed;
            detail.FindCaptionedModel<DatePickerViewModel>("Transaction Date:").Visibility = Visibility.Visible;
            detail.FindCaptionedModel<TextBoxViewModel>("Expense Type:").Visibility = Visibility.Collapsed;
            var cbvm = detail.FindCaptionedModel<ComboBoxViewModel<ExpenseType>>("Expense Type:");
            cbvm.Visibility = Visibility.Visible;
            detail.FindCaptionedModel<TextBoxViewModel>("Comment:").IsReadOnly = false;
            detail.FindCaptionedModel<TextBoxViewModel>("Amount:").IsReadOnly = false;

            cbvm.ClearItems();
            using (var dc = Context.GetContextInstance())
            {
                foreach (var et in dc.ExpenseTypes)
                {
                    cbvm.Add(et, et.Description);
                }
            }

            this.ResetCommands();

            this.cancelAction =
                () =>
                {
                    this.IsInSelectionMode = true;
                    this.List.Remove(lvivm);
                    if (last == null)
                    {
                        this.ResetCommands();
                        return;
                    }

                    this.List.RaiseSelectionChangedEvent = false;
                    last.IsSelected = true;
                    this.List.RaiseSelectionChangedEvent = true;
                    this.List.InvokeSelectionChanged();
                };
        }

        private void DoSave()
        {
            var detail = this.Details.OfType<RecordDetailViewModel>().First();
            var data = this.List.SelectedData;

            data.Date = detail.FindCaptionedModel<DatePickerViewModel>("Transaction Date:").DateValue;
            data.ExpenseTypeId =
                detail.FindCaptionedModel<ComboBoxViewModel<ExpenseType>>("Expense Type:").SelectedData.Id;
            data.Comment = detail.FindCaptionedModel<TextBoxViewModel>("Comment:").Value;
            data.Amount = decimal.Parse(detail.FindCaptionedModel<TextBoxViewModel>("Amount:").Value);
            using (var scope = new TransactionScope())
            {
                using (var dc = Context.GetContextInstance())
                {
                    if (data.Id == 0)
                    {
                        dc.AddToExpenses(data);
                    }
                    else
                    {
                        var found = dc.Expenses.Single(item => item.Id == data.Id);
                        if (found.Date != data.Date)
                        {
                            found.Date = data.Date;
                        }

                        if (found.ExpenseTypeId != data.ExpenseTypeId)
                        {
                            found.ExpenseTypeId = data.ExpenseTypeId;
                        }

                        if (found.Comment != data.Comment)
                        {
                            found.Comment = data.Comment;
                        }

                        if (found.Amount != data.Amount)
                        {
                            found.Amount = data.Amount;
                        }

                        data = found;
                    }

                    // Try to find period allocation to match
                    var alloc =
                        dc.PeriodAllocations.SingleOrDefault(
                            item => item.Period.Start >= data.Date && item.Period.End <= data.Date);
                    if (alloc != null)
                    {
                        data.PeriodAllocations.Add(alloc);
                    }

                    dc.SaveChanges();
                }

                scope.Complete();
            }

            this.RefreshList();
        }

        private void DoCancel()
        {
            if (this.cancelAction == null)
            {
                return;
            }

            this.cancelAction();
            this.cancelAction = null;
        }

        private void ClearDetails()
        {
            while (this.Details.Count > 0)
            {
                this.RemoveLastDetail();
            }
        }

        private void RemoveLastDetail()
        {
            using (var last = this.Details.Last())
            {
                this.Details.Remove(last);
            }
        }

        private void ResetCommands()
        {
            var detail = this.Details.OfType<RecordDetailViewModel>().FirstOrDefault();
            SetVisibility(
                detail == null || this.IsInSelectionMode ? Visibility.Visible : Visibility.Collapsed,
                this.NewCommand);

            SetVisibility(
                !this.IsInSelectionMode ? Visibility.Visible : Visibility.Collapsed,
                this.SaveCommand,
                this.CancelCommand);
        }

        private static void SetVisibility(Visibility visibility, params CommandViewModel[] commands)
        {
            commands.ForEach(cmd => cmd.Visibility = visibility);
        }

        #endregion Methods
    }
}
